{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "initial_id",
   "metadata": {
    "collapsed": true,
    "ExecuteTime": {
     "end_time": "2024-05-18T06:08:09.391306600Z",
     "start_time": "2024-05-18T06:08:09.386305300Z"
    }
   },
   "outputs": [],
   "source": [
    "# imports\n",
    "import re\n",
    "import time\n",
    "\n",
    "from bs4 import NavigableString, Tag\n",
    "from bs4 import BeautifulSoup\n",
    "import markdown2\n",
    "import hashlib\n",
    "import shutil\n",
    "import os"
   ]
  },
  {
   "cell_type": "code",
   "outputs": [],
   "source": [
    "# constants\n",
    "INPUT_FILEPATH = r\"C:\\Users\\tianfy\\Desktop\\毕设\\markdown\\毕设.md\"\n",
    "OUTPUT_FILEPATH = INPUT_FILEPATH.replace(\".md\", \".txt\")\n",
    "PIC_OUTPUT_DIR = os.path.join(os.path.dirname(INPUT_FILEPATH), \"figures\")\n",
    "MD5 = lambda x: hashlib.md5(x.encode(\"utf-8\")).hexdigest()\n",
    "if not os.path.exists(PIC_OUTPUT_DIR):\n",
    "    os.makedirs(PIC_OUTPUT_DIR)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-05-18T06:08:09.409633500Z",
     "start_time": "2024-05-18T06:08:09.392305500Z"
    }
   },
   "id": "b35c1798f613f93d",
   "execution_count": 14
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "45069\n"
     ]
    }
   ],
   "source": [
    "with open(INPUT_FILEPATH, \"r\", encoding=\"utf-8\") as f:\n",
    "    file = f.read()\n",
    "\n",
    "pic_list = []\n",
    "pre_render_code_blocks = {}\n",
    "pre_render_inline_math = {}\n",
    "char_statistic = 0\n",
    "li_count = 0\n",
    "def parse(tag) -> str:\n",
    "    global li_count\n",
    "    if isinstance(tag, NavigableString):\n",
    "        return tag.string\n",
    "    elif isinstance(tag, Tag):\n",
    "        if tag.name == \"p\":\n",
    "            string = \"\"\n",
    "            try:\n",
    "                for child in tag:\n",
    "                    string += parse(child)\n",
    "            except Exception as e:\n",
    "                print(tag)\n",
    "                raise e\n",
    "            return f\"{string}\\n\"\n",
    "        elif tag.name == \"strong\":\n",
    "            string = \"\"\n",
    "            for child in tag:\n",
    "                string += parse(child)\n",
    "            return f\"\\\\textbf{{{string}}}\"\n",
    "        elif tag.name == \"h1\":\n",
    "            string = \"\"\n",
    "            for child in tag:\n",
    "                string += parse(child)\n",
    "            return f\"\\\\chapter{{{string}}}\"\n",
    "        elif tag.name == \"h2\":\n",
    "            string = \"\"\n",
    "            for child in tag:\n",
    "                string += parse(child)\n",
    "            return f\"\\\\section{{{string}}}\"\n",
    "        elif tag.name == \"h3\":\n",
    "            string = \"\"\n",
    "            for child in tag:\n",
    "                string += parse(child)\n",
    "            return f\"\\\\subsection{{{string}}}\"\n",
    "        elif tag.name == \"ol\":\n",
    "            li_count = 1\n",
    "            string = \"\"\n",
    "            for child in tag:\n",
    "                string += parse(child)\n",
    "            # return f\"\\\\begin{{enumerate}}[itemsep=0em,itemindent=3em, leftmargin=0em, topsep=0em]\\n{string}\\\\end{{enumerate}}\\n\"\n",
    "            return f\"{string}\"\n",
    "        elif tag.name == \"ul\":\n",
    "            li_count = -1\n",
    "            string = \"\"\n",
    "            for child in tag:\n",
    "                string += parse(child)\n",
    "            return f\"\\\\begin{{itemize}}[itemsep=0em,itemindent=3em, leftmargin=0em, topsep=0em]\\n{string}\\\\end{{itemize}}\\n\"\n",
    "        elif tag.name == \"li\":\n",
    "            string = \"\"\n",
    "            for child in tag:\n",
    "                string += parse(child)\n",
    "            if li_count < 0:\n",
    "                return f\"\\\\item {string}\\n\"\n",
    "            string = f\"{li_count}. {string}\\n\"\n",
    "            li_count += 1\n",
    "            return string\n",
    "        elif tag.name == \"code\":\n",
    "            string = tag.string\n",
    "            splitlines = string.splitlines()\n",
    "            language = splitlines[0]\n",
    "            caption = splitlines[1]\n",
    "            style = None\n",
    "            if caption.startswith(\"[\") and caption.endswith(\"]\"):\n",
    "                caption = caption[1:-1]\n",
    "                if \"|\" in caption:\n",
    "                    caption, style = caption.split(\"|\")\n",
    "                content = \"\\n\".join(splitlines[2:])\n",
    "            else:\n",
    "                content = \"\\n\".join(splitlines[1:])\n",
    "            attrs = {\n",
    "            }\n",
    "            if style is not None:\n",
    "                attrs[\"style\"] = f\"style{style}\"\n",
    "            def attrs_2_str(attrs):\n",
    "                return \",\".join([f\"{k}={v}\" if v is not None else f\"{k}\" for k, v in attrs.items()])\n",
    "            \n",
    "            if language == \"tex\":\n",
    "                string = content\n",
    "            else:\n",
    "                if language != \"txt\" and language:\n",
    "                    attrs[\"language\"] = language\n",
    "                string = f\"\"\"\n",
    "\\\\begin{{figure}}[!h]\n",
    "\\\\centering\n",
    "\\\\begin{{lstlisting}}[{attrs_2_str(attrs)}]\\n{content}\\n\\\\end{{lstlisting}}\n",
    "\\\\caption{{{caption}}} % QAQ\n",
    "\\\\label{{{caption}}}\n",
    "\\\\end{{figure}}\"\"\"\n",
    "\n",
    "            key = f\"[{MD5(string)}]\"\n",
    "            pre_render_code_blocks[key] = string\n",
    "            return key\n",
    "        elif tag.name == \"blockquote\":\n",
    "            return \"\"\n",
    "        elif tag.name == \"img\":\n",
    "            name = tag[\"alt\"].split(\".\")[0]\n",
    "            ext = tag[\"src\"].split(\".\")[-1]\n",
    "            key = f\"{len(pic_list)}_{MD5(name)}\"\n",
    "\n",
    "            new_name = f\"{key}.{ext}\"\n",
    "            pic_list.append((new_name, tag[\"src\"]))\n",
    "            lw = \"\" if \"lw\" not in tag.attrs else tag[\"lw\"]\n",
    "            template = rf\"\"\"\n",
    "            %% autogen by script\n",
    "            \\begin{{figure}}[!htbp]\n",
    "            \\centering\n",
    "            \\{ 'includesvg' if ext == 'svg' else 'includegraphics' }[width={lw}\\linewidth]{{figures/{key}.{ext}}}\n",
    "            \\caption{{{key}}}\n",
    "            \\label{{{key}}}\n",
    "            \\end{{figure}}\"\"\"\n",
    "            template = \"\\n\".join([x.strip() for x in template.splitlines()])\n",
    "            return template\n",
    "        elif tag.name == \"em\":\n",
    "            # WARNING: this is not a good way to handle em\n",
    "            return f\"_{tag.string}_\"\n",
    "        elif tag.name == \"pre\":\n",
    "            string = \"\"\n",
    "            for child in tag:\n",
    "                string += parse(child)\n",
    "            return string\n",
    "        else:\n",
    "            print(f\"unknown tag: {tag.name} {tag}\")\n",
    "            assert False\n",
    "\n",
    "def pre_process(string: str) -> str:\n",
    "    return string\n",
    "\n",
    "def post_process(string: str) -> str:\n",
    "    # newline popup\n",
    "    def float_up_newline(s):\n",
    "        black_list = [\"\\\\item\", \"\\\\textbf\"]\n",
    "        white_list = [\"\\\\\",\"[\" ,\"%\"]\n",
    "        is_skip = lambda x: any([x.startswith(y) for y in white_list]) and not any([x.startswith(y) for y in black_list])\n",
    "        lines = s.split('\\n')\n",
    "        i = 0\n",
    "        while i < len(lines):\n",
    "            if lines[i] == '\\\\newline':\n",
    "                j = i\n",
    "                while j > 0 and (not lines[j-1].strip() or is_skip(lines[j-1])):\n",
    "                    lines[j], lines[j-1] = lines[j-1], lines[j]\n",
    "                    j -= 1 \n",
    "            i += 1\n",
    "        return '\\n'.join(lines)\n",
    "    \n",
    "    # # shift up \\newline\n",
    "    # string = float_up_newline(string)\n",
    "    #\n",
    "    # # remove duplicate \\newline\n",
    "    # for i in range(50):\n",
    "    #     string = string.replace(\"\\\\newline\", \"\\\\newline\")\n",
    "        \n",
    "    # remove first line \\newline\n",
    "    lines = string.split(\"\\n\")\n",
    "    if lines[0] == \"\\\\newline\":\n",
    "        lines = lines[1:]\n",
    "    string = \"\\n\".join(lines)\n",
    "    \n",
    "    # process figure\n",
    "    string = re.sub(\n",
    "        r\"\\\\caption{(.*)}\\n\\\\label{(.*)}([\\s\\S]*?)\\n\\[(.*)]\",\n",
    "        r\"\\\\caption{\\4}\\n\\\\label{\\4}\\3\",\n",
    "        string,\n",
    "        flags=re.MULTILINE)\n",
    "    \n",
    "    # process %\n",
    "    magic_key = \"y123j45h6gw7c8d9\"\n",
    "    string = string.replace(\"%%\", magic_key)\n",
    "    string = string.replace(\"%\", \"\\\\%\")\n",
    "    string = string.replace(magic_key, \"%\")\n",
    "    \n",
    "    # process codeblock\n",
    "    for key, value in pre_render_code_blocks.items():\n",
    "        string = string.replace(key, value)\n",
    "    string = string.replace(r\"\\codespace\",r\"\\vspace{3pt}\")\n",
    "        \n",
    "    # process ext\n",
    "    string = f\"% generate time: {time.strftime('%Y-%m-%d %H:%M:%S')}\\n\" + string\n",
    "    \n",
    "    return string\n",
    "\n",
    "\n",
    "file = pre_process(file)\n",
    "html = markdown2.markdown(file)\n",
    "soup = BeautifulSoup(html, \"html.parser\")\n",
    "string = \"\"\n",
    "for child in soup:\n",
    "    string += parse(child)\n",
    "string = post_process(string)\n",
    "\n",
    "# move picture\n",
    "for pic in pic_list:\n",
    "    name, src = pic\n",
    "    target = os.path.join(PIC_OUTPUT_DIR, name)\n",
    "    \n",
    "    # Check if target file exists and its size is the same as the source file\n",
    "    if os.path.exists(target) and os.path.getsize(target) == os.path.getsize(src):\n",
    "        continue    \n",
    "\n",
    "    shutil.copyfile(src, target)\n",
    "\n",
    "\n",
    "# output to inputfile directory\n",
    "with open(OUTPUT_FILEPATH, \"w\", encoding=\"utf-8\") as f:\n",
    "    f.write(string)\n",
    "\n",
    "def is_chinese(char):\n",
    "    if '\\u4e00' <= char <= '\\u9fff':\n",
    "        return True\n",
    "    else:\n",
    "        return False   \n",
    "print(\n",
    "    len(\n",
    "        [x for x in string if is_chinese(x)]\n",
    "    )\n",
    ")"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-05-18T06:08:09.495823200Z",
     "start_time": "2024-05-18T06:08:09.410635Z"
    }
   },
   "id": "6497c699f4e0ae4b",
   "execution_count": 15
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "'<p>通过手动检查了Evosuite未能成功覆盖的分支（图中上方焦点方法）以及由两种方法生成的测试用例（图中下方测试用例），可以看到该焦点方法的主要功能是从一个完全限定的类名字符串中提取简短的类名，涉及到的情况有空字符处理、数组类型处理、基本类型缩写的处理等。Evosuite未覆盖到的分支已在图中标出，如果要进入这个分支，需要参数满足以下几个条件：</p>\\n\\n<ol>\\n<li>className 必须以字符“[”开始，这表示它是一个数组类型。这是进入外层的if条件所必须的。</li>\\n<li>在移除了开头的“[”字符后，如果元素类型是引用数据类型，那么它的内部表示会以字符“L”开始，并以字符“;”结尾。这是 JVM 的内部表示方式，其中“L”表示后续字符是一个类的全限定名，“;”是这个名字的结尾。</li>\\n</ol>\\n\\n<p>所以，要满足进入这个if分支的条件，原始的className字符串应该是一个表示引用数据类型数组的内部表示。例如 “[Ljava.lang.String;”对应于String[]类型。</p>\\n'"
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "html = markdown2.markdown(\n",
    "    r\"\"\"\n",
    "通过手动检查了Evosuite未能成功覆盖的分支（图中上方焦点方法）以及由两种方法生成的测试用例（图中下方测试用例），可以看到该焦点方法的主要功能是从一个完全限定的类名字符串中提取简短的类名，涉及到的情况有空字符处理、数组类型处理、基本类型缩写的处理等。Evosuite未覆盖到的分支已在图中标出，如果要进入这个分支，需要参数满足以下几个条件：\n",
    "\n",
    "1. className 必须以字符“[”开始，这表示它是一个数组类型。这是进入外层的if条件所必须的。\n",
    "2. 在移除了开头的“[”字符后，如果元素类型是引用数据类型，那么它的内部表示会以字符“L”开始，并以字符“;”结尾。这是 JVM 的内部表示方式，其中“L”表示后续字符是一个类的全限定名，“;”是这个名字的结尾。\n",
    "\n",
    "所以，要满足进入这个if分支的条件，原始的className字符串应该是一个表示引用数据类型数组的内部表示。例如 “[Ljava.lang.String;”对应于String[]类型。\n",
    "\n",
    "    \"\"\"\n",
    ")\n",
    "html"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-05-18T06:08:09.501462400Z",
     "start_time": "2024-05-18T06:08:09.495823200Z"
    }
   },
   "id": "177fc481562ac00b",
   "execution_count": 16
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "[Modifier \"public\" line 2, position 1,\n Keyword \"class\" line 2, position 8,\n Identifier \"ASD\" line 2, position 14,\n Separator \"{\" line 2, position 17,\n Modifier \"public\" line 3, position 5,\n Modifier \"static\" line 3, position 12,\n Keyword \"void\" line 3, position 19,\n Identifier \"main\" line 3, position 24,\n Separator \"(\" line 3, position 28,\n Identifier \"String\" line 3, position 29,\n Separator \"[\" line 3, position 35,\n Separator \"]\" line 3, position 36,\n Identifier \"args\" line 3, position 38,\n Separator \")\" line 3, position 42,\n Separator \"{\" line 3, position 43,\n Identifier \"System\" line 4, position 9,\n Separator \".\" line 4, position 15,\n Identifier \"out\" line 4, position 16,\n Separator \".\" line 4, position 19,\n Identifier \"println\" line 4, position 20,\n Separator \"(\" line 4, position 27,\n String \"'{'\" line 4, position 28,\n Separator \")\" line 4, position 31,\n Separator \";\" line 4, position 32,\n Separator \"}\" line 5, position 5,\n Separator \"}\" line 6, position 1]"
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import javalang\n",
    "g = javalang.tokenizer.tokenize(r\"\"\"\n",
    "public class ASD{\n",
    "    public static void main(String[] args){\n",
    "        System.out.println('{'); // awd \n",
    "    }\n",
    "}\n",
    "\n",
    "\"\"\")\n",
    "list(g)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-05-18T06:08:09.514797900Z",
     "start_time": "2024-05-18T06:08:09.501462400Z"
    }
   },
   "id": "a750d935fba3d1cd",
   "execution_count": 17
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 N\n",
      "2 N\n",
      "3 Y\n",
      "4 N\n",
      "5 N\n"
     ]
    }
   ],
   "source": [
    "def find_quick_rises(prices):\n",
    "    from collections import defaultdict, deque\n",
    "\n",
    "    # 存储每分钟的价格\n",
    "    price_by_minute = defaultdict(list)\n",
    "    for minute, price in prices:\n",
    "        price_by_minute[minute].append(price)\n",
    "\n",
    "    # 使用一个deque来存储最近三分钟的价格的最小值\n",
    "    min_prices = deque()\n",
    "    # 存储最近三分钟的价格\n",
    "    recent_prices = deque()\n",
    "\n",
    "    current_min = float('inf')\n",
    "    outputs = []\n",
    "\n",
    "    # 从第一分钟开始处理到最后一分钟\n",
    "    last_minute = max(price_by_minute.keys())\n",
    "    for minute in range(1, last_minute + 1):\n",
    "        if minute not in price_by_minute:\n",
    "            # 没有这一分钟的数据，按照连续假设，这种情况应该不会发生\n",
    "            continue\n",
    "\n",
    "        # 当前分钟的价格列表\n",
    "        current_prices = price_by_minute[minute]\n",
    "\n",
    "        # 更新最近三分钟的价格信息\n",
    "        recent_prices.extend(current_prices)\n",
    "        current_min = min(current_min, min(current_prices))\n",
    "\n",
    "        # 维护长度为3的窗口（对应时间为minute-2到minute）\n",
    "        if len(recent_prices) > 3:\n",
    "            # 移除超出三分钟的价格\n",
    "            old_prices = price_by_minute[minute - 3]\n",
    "            for old_price in old_prices:\n",
    "                recent_prices.popleft()\n",
    "                if old_price == current_min:\n",
    "                    # 如果移除的价格是当前最小值，需要重新计算最小值\n",
    "                    current_min = min(recent_prices)\n",
    "\n",
    "        # 检查是否有价格比前三分钟的最小价格至少高出5\n",
    "        rise_detected = 'N'\n",
    "        if minute > 2:  # 从第3分钟开始才有三分钟的数据\n",
    "            for price in current_prices:\n",
    "                if price >= current_min + 5:\n",
    "                    rise_detected = 'Y'\n",
    "                    break\n",
    "\n",
    "        # 记录结果\n",
    "        outputs.append(f\"{minute} {rise_detected}\")\n",
    "\n",
    "    return outputs\n",
    "\n",
    "# 输入数据\n",
    "prices = [\n",
    "    (1, 1),\n",
    "    (2, 5),\n",
    "    (2, 4),\n",
    "    (2, 5),\n",
    "    (3, 7),\n",
    "    (3, 5),\n",
    "    (3, 4),\n",
    "    (4, 4),\n",
    "    (5, 7),\n",
    "    (5, 4)\n",
    "]\n",
    "\n",
    "# 执行函数\n",
    "results = find_quick_rises(prices)\n",
    "for result in results:\n",
    "    print(result)\n"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-05-18T06:08:09.576890100Z",
     "start_time": "2024-05-18T06:08:09.513798100Z"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [],
   "metadata": {
    "collapsed": false
   },
   "id": "4238618de65aad83"
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
